package com.power.config;

import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson.JSONObject;
import com.power.contant.AuthConstants;
import com.power.contant.BussinessEnum;
import com.power.impl.UserDetailServiceImpl;
import com.power.model.LoginResult;
import com.power.model.Result;
import com.power.model.SecurityUser;
import com.power.util.JSONUtils;
import com.power.util.ResponseUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AccountStatusException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import javax.annotation.Resource;
import java.time.Duration;

/**
 * 配置列
 */
@Configuration
@Slf4j
public class AuthSecurityConfig extends WebSecurityConfigurerAdapter {


    @Resource
    private UserDetailServiceImpl userDetailService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 自定义的安全认证框架
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //启动时候生成的密码是内存类中的
        auth.userDetailsService(userDetailService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //关闭跨站请求伪造
        http.csrf().disable();
        //关闭跨域
        http.cors().disable();
        //关闭session的使用策略 不适用session 机制
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        //配置登录信息
        http.formLogin().loginProcessingUrl(AuthConstants.LOGIN_URL) //自定义登录页
                .successHandler(authenticationSuccessHandler())
                .failureHandler(authenticationFailureHandler());

        // 配置登出信息
        http.logout()
                .logoutUrl(AuthConstants.LOGOUT_URL)
                .logoutSuccessHandler(logoutSuccessHandler());

        //配置其他系统 都需要 进行认证
        http.authorizeHttpRequests().anyRequest().authenticated();
    }

    @Bean //登录成功的处理器
    public AuthenticationSuccessHandler authenticationSuccessHandler() {
        return (request, response, authentication) -> {


            //使用UUID作为用户的唯一的表示
            String token = UUID.randomUUID().toString();
            // 获取安全用户的认证对象 （对象在Security的上下文中）
            SecurityUser securityUser = (SecurityUser) authentication.getPrincipal();
            //将安全认证的用户的对象转换为JSON格式的字符串 (这里使用的fastJSON)
            String securityUserJsonStr = JSONObject.toJSONString(securityUser);
            // redis 中的 key 为token ， value 为 JSON字符串
            stringRedisTemplate.opsForValue().set(AuthConstants.LOGIN_TOKEN_PREFIX + token, securityUserJsonStr, Duration.ofSeconds(AuthConstants.TOKEN_TIME));

            //将创建的数据返回给前端（token和有效的时间）
            LoginResult loginResult = new LoginResult(token, AuthConstants.TOKEN_TIME);
            //创建统一的响应的结果对象
            Result<Object> result = Result.success(loginResult);

            //将信息返回给前端
            ResponseUtils.write(response, JSONUtils.objToJson(result));

        };
    }

    @Bean //登录失败的处理器
    public AuthenticationFailureHandler authenticationFailureHandler() {
        return (request, response, exception) -> {


            //创建统一的响应的结果对象
            Result<String> result = new Result<>();
            result.setCode(BussinessEnum.LOGIN_ERROR.getCode());

            if (exception instanceof BadCredentialsException) {
                result.setMessage("用户名或者密码错误");
            } else if (exception instanceof InternalAuthenticationServiceException) {
                result.setMessage(exception.getMessage());
            } else if (exception instanceof UsernameNotFoundException) {
                result.setMessage("用户名或者密码错误");
            } else if (exception instanceof AccountStatusException) {
                result.setMessage("账号状态异常");
            } else {
                result.setMessage(BussinessEnum.LOGIN_ERROR.getDesc());
            }
            ResponseUtils.write(response, JSONUtils.objToJson(result));
        };
    }

    @Bean //登录失败的处理器
    public LogoutSuccessHandler logoutSuccessHandler() {
        return (request, response, authentication) -> {
            String authorizationValue = request.getHeader(AuthConstants.AUTHORIZATION);
            String token = authorizationValue.replaceFirst(AuthConstants.BEARER, "");
            stringRedisTemplate.delete(token);
            Result<Object> result = Result.success(null);
            ResponseUtils.write(response, JSONUtils.objToJson(result));

        };
    }

}
